package cn.dengta.webapp.trade.service;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import cn.dengta.common.context.Env;
import cn.dengta.common.util.Common;
import cn.dengta.context.model.RedisKey;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.base.util.MybatisUtil;
import cn.dengta.webapp.trade.dao.UserIntimacyDao;
import cn.dengta.webapp.trade.entity.UserIntimacy;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class UserIntimacyService extends BaseServiceImpl<UserIntimacy, UserIntimacyDao, Long> {

	@Resource
	RedisTemplate<String, Object> redisTemplate;

	public Integer addUserScore(Long lowUserId, Long highUserId, Integer score) {
		double currentIntimacy = NumberUtil.getDouble(redisTemplate.opsForZSet().incrementScore(RedisKey.INTIMACY_COMMON, Common.sortKey(lowUserId, highUserId, null), score), 0D);
		int s = NumberUtil.getInt(currentIntimacy);
		this.baseDao.addUserScore(lowUserId, highUserId, s);
		return s;
	}

	public Integer findScoreByUser(Long lowUserId, Long highUserId) {
		UserIntimacy ui = new UserIntimacy();
		ui.setLowUserId(lowUserId);
		ui.setHighUserId(highUserId);
		UserIntimacy intimacy = this.selectOne(new QueryWrapper<>(ui));
		return intimacy == null ? 0 : intimacy.getScore();
	}

	public Long countScore(Long userId, Integer score) {
		final QueryWrapper<UserIntimacy> wrapper = Wrappers.query();
		wrapper.apply("(low_user_id = " + userId + " OR high_user_id = " + userId + ")");
		wrapper.ge(UserIntimacy.SCORE, score);
		return count(wrapper);
	}

	//	@PostConstruct
	//	@Transactional(propagation = Propagation.NEVER)
	public void loadRedis() {
		log.info("加载loadRedis亲密度");
		Long count = this.count(new QueryWrapper<>());
		if (Cmp.gt(count, 0)) {
			log.info("加载loadRedis亲密度, 亲密度已同步");
			return;
		}
		Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("loadRedis_intimacy", "1", Duration.ofMinutes(10));
		if (Boolean.FALSE.equals(ifAbsent)) {
			log.info("加载loadRedis亲密度, 未获取到同步锁");
			return;
		}
		int size = 0;
		int batchSize = Env.inProduction() ? 1000 : 100;
		Cursor<ZSetOperations.TypedTuple<Object>> cursor = null;
		try {
			List<UserIntimacy> uiData = new ArrayList<>(batchSize);
			cursor = redisTemplate.opsForZSet().scan(RedisKey.INTIMACY_COMMON, ScanOptions.scanOptions().count(batchSize).build());
			while (cursor.hasNext()) {
				size++;
				ZSetOperations.TypedTuple<Object> tuple = cursor.next();
				String string = tuple.getValue().toString();
				List<Long> list = StringUtil.split(string, "-", Long::parseLong);
				uiData.add(UserIntimacy.of(list.get(0), list.get(1), NumberUtil.getInt(tuple.getScore())));
				if (uiData.size() == batchSize) {
					log.info("开始批次持久化数据：数据大小={} .....", size);
					this.baseDao.batchInitUserScore(uiData);
					uiData.clear();
				}
			}
			if (uiData.size() > 0) {
				log.info("开始批次持久化数据结束：数据大小={} .....", size);
				this.baseDao.batchInitUserScore(uiData);
			}
		} catch (Exception e) {
			log.error("加载loadRedis亲密度", e);
		} finally {
			log.info("加载loadRedis亲密度 持久化完成 数据大小={}", size);
			redisTemplate.delete("loadRedis_intimacy");
			cursor.close();
		}
	}

	public Map<Long, Integer> findByUserIds(Long userId, Collection<Long> toUserIds) {
		if (userId == null || !X.isValid(toUserIds)) {
			return Collections.emptyMap();
		}
		String idSql = MybatisUtil.inIdSql("", toUserIds.toArray(Long[]::new));
		List<UserIntimacy> list = this.baseDao.findByUserIds(userId, idSql);
		return list.stream().collect(Collectors.toMap(UserIntimacy::getLowUserId, UserIntimacy::getScore));
	}

}
